package com.bluexmicro.bluetooth.peripheral.handler

import android.annotation.SuppressLint
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothStatusCodes.*
import android.os.Build
import com.bluexmicro.bluetooth.LogUtils
import com.bluexmicro.bluetooth.bluetoothStatusCodesName
import com.bluexmicro.bluetooth.model.GattStatus
import com.bluexmicro.bluetooth.model.WriteType
import com.bluexmicro.bluetooth.toHexString
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

typealias WriteCallback = (
    gatt: BluetoothGatt,
    characteristic: BluetoothGattCharacteristic,
    status: Int
) -> Unit

@SuppressLint("MissingPermission")
class BatchWriteTasksHandler {

    private var callback: WriteCallback? = null
    private var cancellableContinuation: CancellableContinuation<Result<Any>>? = null

    fun onCharacteristicWrite(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        status: Int
    ) {
        callback?.invoke(gatt, characteristic, status)
    }

    private fun setCallback(cb: WriteCallback) {
        this.callback = cb
    }

    fun isActive(): Boolean {
        return cancellableContinuation?.isActive == true
    }

    //todo timeout 断连后请求
    suspend fun execute(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        tasks: List<ByteArray>
    ): Result<Any> = suspendCancellableCoroutine {
        cancellableContinuation = it
        val pendingTasks = mutableListOf<ByteArray>()
        pendingTasks.addAll(tasks)
        setCallback { _, c, status ->
            //过滤掉其他callback
            if (!c.uuid.equals(characteristic.uuid)) return@setCallback
            if (status != BluetoothGatt.GATT_SUCCESS) {
                this.callback = null
                if (it.isActive) {
                    val err = GattStatus.fromValue(status).name
                    it.resume(Result.failure(IllegalStateException(err)))
                }
            } else {
                if (pendingTasks.isNotEmpty()) {
                    val task = pendingTasks.removeAt(0)
                    if (it.isActive) {
                        val err = write(gatt, characteristic, task)
                        if (err != null) {
                            this.callback = null
                            it.resume(Result.failure(IllegalStateException(err)))
                        }
                    }
                } else {
                    this.callback = null
                    if (it.isActive) it.resume(Result.success(Unit))
                }
            }
        }
        //首次
        if (it.isActive) {
            if (pendingTasks.isNotEmpty()) {
                val task = pendingTasks.removeAt(0)
                val err = write(gatt, characteristic, task)
                if (err != null) {
                    this.callback = null
                    it.resume(Result.failure(IllegalStateException(err)))
                }
            } else {
                it.resume(Result.failure(IllegalStateException("Write Task is empty!")))
            }
        }

        it.invokeOnCancellation {
            this.callback = null
            pendingTasks.clear()
        }
    }

    private fun write(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        value: ByteArray
    ): String? {
        val res: Boolean
        val detail: String
        val type = WriteType.WithoutResponse
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            val code = gatt.writeCharacteristic(characteristic, value, type.value)
            res = code == SUCCESS
            detail = code.bluetoothStatusCodesName()
        } else {
            characteristic.writeType = type.value
            characteristic.value = value
            res = gatt.writeCharacteristic(characteristic)
            detail = if (res) "" else "Write was rejected by ststem"
        }

        LogUtils.sendLog(
            mapOf(
                "mac" to gatt.device.address,
                "action" to "callWrite",
                "service" to characteristic.service.uuid.toString(),
                "characteristic" to characteristic.uuid.toString(),
                "WriteType" to type.name,
                "value" to value.toHexString(),
                "detail" to detail
            )
        )

        return if (!res) {
            this.callback = null
            detail
        } else {
            null
        }
    }
}